use std::os::fd::AsRawFd;
use tokio::net::*;
use tokio::runtime::*;

type Fd = TcpStream;

mod http;
use http::*;

async fn server_conn(conn: Fd, size: usize) {
    let _ = conn.set_linger(Some(Duration::default()));
    loop {
        let mut req = HttpReq::new();
        if let Err(_) = req.read(&conn).await {
            break;
        }

        let rsp = HttpRsp::new(size);
        if let Err(_) = rsp.write(&conn).await {
            break;
        }
    }
    unsafe { libc::shutdown(conn.as_raw_fd(), libc::SHUT_RDWR) };
}

async fn server(port: u16, size: usize) {
    let addr = format!("127.0.0.1:{}", port);
    let server = match TcpListener::bind(addr).await {
        Ok(server) => server,
        Err(e) => {
            println!("tcp_server fail: {e:?}");
            return;
        }
    };
    loop {
        match server.accept().await {
            Ok((conn, _)) => {
                let _ = tokio::spawn(server_conn(conn, size));
            }
            Err(_e) => {}
        };
    }
}

use std::time::*;

fn help() {
    println!("{} options", hictor::program_invocation_name());
    println!("options:");
    println!("--addr | -a: listening addr, format is port | ip:port");
    println!("--threads | -t: default is the number of cores available to the system");
    println!("--size | -s: body_length");
    println!("--help | -h: print help message");
}

fn main() {
    let opts = hiopt::options!["addr:", "a:", "size:", "s:", "threads:", "t:", "help", "h"];
    let args = unsafe { hiopt::raw_args_from_i8(hictor::args()) };
    let mut port = 2001_u16;
    let mut size = 1024_usize;
    let mut nth = 0_usize;
    for opt in opts.opt_iter(&args[1..]) {
        let (optidx, optval) = opt.unwrap();
        match optidx {
            0..=1 => port = optval.unwrap().parse().unwrap(),
            2..=3 => size = optval.unwrap().parse().unwrap(),
            4..=5 => nth = optval.unwrap().parse().unwrap(),
            6..=7 => return help(),
            _ => unreachable!(),
        }
    }
    let rt = if nth == 0 {
        Builder::new_multi_thread().enable_all().build().unwrap()
    } else {
        Builder::new_multi_thread()
            .enable_all()
            .worker_threads(nth)
            .build()
            .unwrap()
    };
    let _ = rt.block_on(server(port, size));
}
